%{
#include <cstddef>
#include <cstdio>
#include <string.h>  

#include "../src/type/Type.hpp"
#include "../src/SymbTable.hpp"
#include "../src/IdentTable.hpp"
#include "../inc/Code3ad.hpp"
#include "categorie/Variable.hpp"
#include <string>

using std::string;
extern IdentTable t;
extern SymbTable ts;
extern int yyerror ( char* );
extern int yylex ();


%}
%union{
    int intval;
    char * strval;
    float fval;
    c3a retour;
	int ident;
	SymbTable * ts_c;
	LexToBison ltb;
}
%type<retour>Expression;
%type<retour>MathExpr;
%type<retour>BoolExpr;
%type<retour>AtomExpr;
%type<retour>VarExpr;
%type<retour>Call;



%token KW_PROGRAM
%token KW_CONST
%token KW_TYPE
%token KW_VAR
%token KW_ARRAY
%token KW_OF
%token KW_RECORD
%token KW_BEGIN
%token KW_END
%token KW_DIV
%token KW_MOD
%token KW_AND
%token KW_OR
%token KW_XOR
%token KW_NOT
%token KW_IF
%token KW_THEN
%token KW_ELSE
%token KW_WHILE
%token KW_DO
%token KW_REPEAT
%token KW_UNTIL
%token KW_FOR
%token KW_TO
%token KW_DOWNTO
%token KW_PROC
%token KW_FUNC
%token KW_NIL
%token KW_INTEGER
%token KW_REAL
%token KW_BOOLEAN
%token KW_CHAR
%token KW_STRING
%token SEP_SCOL
%token SEP_DOT
%token SEP_DOTS
%token SEP_DOTDOT
%token SEP_COMMA
%token SEP_CO
%token SEP_CF
%token SEP_PO
%token SEP_PF
%token OP_EQ
%token OP_NEQ
%token OP_LT
%token OP_LTE
%token OP_GT
%token OP_GTE
%token OP_ADD
%token OP_SUB
%token OP_MUL
%token OP_SLASH
%token OP_EXP
%token OP_PTR
%token OP_AFFECT
%token<ltb> TOK_IDENT
%token TOK_INTEGER
%token TOK_REAL
%token TOK_BOOLEAN
%token TOK_STRING
%start Program
%nonassoc OP_EQ OP_NEQ OP_GT OP_LT OP_GTE OP_LTE
%left OP_ADD OP_SUB KW_OR KW_XOR
%left OP_MUL OP_SLASH KW_AND KW_DIV KW_MOD
%right KW_NOT OP_NEG OP_POS
%left OP_EXP
%nonassoc OP_PTR
%nonassoc OP_DOT
%left SEP_CO
%nonassoc KW_IFX
%nonassoc KW_ELSE
%%
    
Program                :    ProgramHeader SEP_SCOL Block SEP_DOT
                ;
ProgramHeader            :    KW_PROGRAM TOK_IDENT
                ;
Block                :    BlockDeclConst BlockDeclType BlockDeclVar BlockDeclFunc BlockCode
                ;
BlockDeclConst            :    KW_CONST ListDeclConst
                 |
                 ;
ListDeclConst            :    ListDeclConst DeclConst
                 |    DeclConst
                 ;
DeclConst            :    TOK_IDENT OP_EQ Expression SEP_SCOL
                 ;
BlockDeclType            :    KW_TYPE ListDeclType
                 |
                 ;
ListDeclType            :    ListDeclType DeclType
                 |    DeclType
                 ;
DeclType            :    TOK_IDENT OP_EQ Type SEP_SCOL
                 ;
BlockDeclVar            :    KW_VAR ListDeclVar
                 |
                 ;
ListDeclVar            :    ListDeclVar DeclVar
                 |    DeclVar
                 ;
DeclVar                :    ListIdent SEP_DOTS Type SEP_SCOL
                 ;
ListIdent            :    ListIdent SEP_COMMA TOK_IDENT
                 |    TOK_IDENT
                 ;
BlockDeclFunc            :    ListDeclFunc SEP_SCOL
                 |
                 ;
ListDeclFunc            :    ListDeclFunc SEP_SCOL DeclFunc
                 |    DeclFunc
                 ;
DeclFunc            :    ProcDecl
                 |    FuncDecl
                 ;
ProcDecl            :    ProcHeader SEP_SCOL Block
                 ;
ProcHeader            :    ProcIdent
                 |    ProcIdent FormalArgs
                 ;
ProcIdent            :    KW_PROC TOK_IDENT
                 ;
FormalArgs            :    SEP_PO ListFormalArgs SEP_PF
                 ;
ListFormalArgs            :    ListFormalArgs SEP_SCOL FormalArg
                 |    FormalArg
                 ;
FormalArg            :    ValFormalArg
                 |    VarFormalArg
                 ;
ValFormalArg            :    ListIdent SEP_DOTS BaseType
                 ;
VarFormalArg            :    KW_VAR ListIdent SEP_DOTS BaseType
                 ;
FuncDecl            :    FuncHeader SEP_SCOL Block
                 ;
FuncHeader            :    FuncIdent FuncResult
                 |    FuncIdent FormalArgs FuncResult
                 ;
FuncIdent            :    KW_FUNC TOK_IDENT
                 ;
FuncResult            :    SEP_DOTS BaseType
                 ;
Type                :    UserType
                 |    BaseType
                 ;
UserType            :    EnumType
                 |    InterType
                 |    ArrayType
                 |    RecordType
                 |    PointerType
                 ;
BaseType            :    TOK_IDENT
                |   KW_INTEGER
                |    KW_REAL
                |    KW_BOOLEAN
                |    KW_CHAR
                |    KW_STRING
                ;
EnumType            :    SEP_PO ListEnumValue SEP_PF
                 ;
ListEnumValue            :    ListEnumValue SEP_COMMA TOK_IDENT
                 |    TOK_IDENT
                 ;
InterType            :    InterBase SEP_DOTDOT InterBase
                 ;
InterBase            :    NSInterBase
                 |    OP_SUB NSInterBase
                 ;
NSInterBase            :    TOK_IDENT
                 |    TOK_INTEGER
                 ;
ArrayType            :    KW_ARRAY SEP_CO ListArrayIndex SEP_CF KW_OF Type
                 ;
ListArrayIndex            :    ListArrayIndex SEP_COMMA ArrayIndex
                |    ArrayIndex
                ;
ArrayIndex            :    TOK_IDENT
                 |    InterType
                 ;
RecordType            :    KW_RECORD RecordFields KW_END
                 ;
RecordFields            :    RecordFields SEP_SCOL RecordField
                 |    RecordField
                 ;
RecordField            :    ListIdent SEP_DOTS Type
                 ;
PointerType            :    OP_PTR Type
                 ;
BlockCode            :    KW_BEGIN ListInstr KW_END
                |    KW_BEGIN ListInstr SEP_SCOL KW_END
                |    KW_BEGIN KW_END
                 ;
ListInstr            :    ListInstr SEP_SCOL Instr
                 |    Instr
                 ;
Instr                :    KW_WHILE Expression KW_DO Instr
                 |    KW_REPEAT ListInstr KW_UNTIL Expression
                 |    KW_FOR TOK_IDENT OP_AFFECT Expression ForDirection Expression KW_DO Instr
                 |    KW_IF Expression KW_THEN Instr %prec KW_IFX
                 |    KW_IF Expression KW_THEN Instr KW_ELSE Instr
                 |    VarExpr OP_AFFECT Expression
                 |    Call
                 |    BlockCode
                 ;
ForDirection            :    KW_TO
                 |    KW_DOWNTO
                 ;
Expression            :    MathExpr 
                 |    CompExpr
                 |    BoolExpr
                 |    AtomExpr {$$ = $1;}
                 |    VarExpr
                |    Call
;
MathExpr            :    Expression OP_ADD Expression {
							// en cas de types différents mais compatible, (int) + (float) ou (float)+(int) on cast les deux en float et on fait l'ajout.
							if($1.calcule && $3.calcule && ( ($1.type == Type::TYPE_INTEGER && $3.type == Type::TYPE_REAL) || ($3.type == Type::TYPE_INTEGER && $1.type == Type::TYPE_REAL) ) ){
								switch($1.type){
									case Type::TYPE_INTEGER :
										$1.type = Type::TYPE_REAL;
										$1.f = $1.i;
										printf("Conversion de $1 (%f) en float\n",$1.f);
										break;
									case Type::TYPE_REAL :
										$3.type = Type::TYPE_REAL;
										$3.f = $3.i;
										printf("Conversion de $2 (%f) en float\n",$3.f);
										break;							
								}
							}
							if($1.calcule && $3.calcule){
								if( $1.type == $3.type){
									switch($1.type){
										case Type::TYPE_INTEGER :
											$$.i = $1.i + $3.i;
											printf("MathExp : (int)%d + (int)%d = (int)%d \n",$1.i,$3.i,$$.i);
											break;
										case Type::TYPE_REAL :
											$$.f = $1.f + $3.f;
											printf("MathExp : (float)%f + (float)%f = (float)%f \n",$1.f,$3.f,$$.f);
											break;
										case Type::TYPE_STRING :
											string stemp = $1.chet;
											stemp += $3.chet;
											size_t size = stemp.size() + 1;
	   										char * buffer = new char[ size ];
											strncpy( buffer, stemp.c_str(), size );
											$$.chet = buffer;
											printf("MathExpr strconcat : %s + %s = %s \n",$1.chet,$3.chet,$$.chet);
											break;								
									}
									$$.type = $1.type;
									$$.calcule = true;
								}
							}else if($1.calcule || $3.calcule){
								// $1 ou $3 calculable
							}else{
								// $1 et $3 non calculables
							}
						}
                 |    Expression OP_SUB Expression
                 |    Expression OP_MUL Expression
                 |    Expression OP_SLASH Expression
                 |    Expression KW_DIV Expression
                 |    Expression KW_MOD Expression
                 |    Expression OP_EXP Expression
                 |    OP_SUB Expression %prec OP_NEG
                 |    OP_ADD Expression %prec OP_POS
                 ;
CompExpr            :    Expression OP_EQ Expression
                 |    Expression OP_NEQ Expression
                 |    Expression OP_LT Expression
                 |    Expression OP_LTE Expression
                 |    Expression OP_GT Expression
                 |    Expression OP_GTE Expression
                 ;
BoolExpr            :    Expression KW_AND Expression {
							if($1.calcule && $3.calcule){
								// $1 et $3 calculables
								if($1.type == Type::TYPE_BOOL && $3.type == Type::TYPE_BOOL){
									if($1.b && $3.b) { 
										$$.b = true;
									}else{
										$$.b = false;
									}
									$$.type == Type::TYPE_BOOL;
									$$.calcule = true;
								}else{
									yyerror( (char * )"$1 && $3 , $1.type ou $3.type != TYPE_BOOL \n");
								}
							}else if($1.calcule || $3.calcule){
								// $1 ou $3 calculable
							}else{
								// $1 et $3 non calculables
							}
						}
                 |    Expression KW_OR Expression{
							if($1.calcule && $3.calcule){
								// $1 et $3 calculables
								if($1.type == Type::TYPE_BOOL && $3.type == Type::TYPE_BOOL){
									if($1.b || $3.b) { 
										$$.b = true;
									}else{
										$$.b = false;
									}
									$$.type == Type::TYPE_BOOL;
									$$.calcule = true;
								}else{
									yyerror( (char * )"$1 || $3 , $1.type ou $3.type != TYPE_BOOL \n");
								}
							}else if($1.calcule || $3.calcule){
								// $1 ou $3 calculable
							}else{
								// $1 et $3 non calculables
							}
						}
                 |    Expression KW_XOR Expression{
							if($1.calcule && $3.calcule){
								// $1 et $3 calculables
								if($1.type == Type::TYPE_BOOL && $3.type == Type::TYPE_BOOL){
									if($1.b != $3.b) { 
										$$.b = true;
									}else{
										$$.b = false;
									}
									$$.type == Type::TYPE_BOOL;
									$$.calcule = true;
								}else{
									yyerror( (char * )"$1 || $3 , $1.type ou $3.type != TYPE_BOOL \n");
								}
							}else if($1.calcule || $3.calcule){
								// $1 ou $3 calculable
							}else{
								// $1 et $3 non calculables
							}
						}
				
                 |    KW_NOT Expression{
							if($2.calcule){
								// $2
								if($2.type == Type::TYPE_BOOL){
									$$.b = !$2.b;
									$$.type == Type::TYPE_BOOL;
									$$.calcule = true;
								}else{
									yyerror( (char * )"$1.type  != TYPE_BOOL \n");
								}
							
							}else{
								// $2 non calculables
							}
						}
                 ;
AtomExpr            :    SEP_PO Expression SEP_PF {$$ = $2;}
                 |    TOK_INTEGER {$$.i = yylval.intval;$$.type = Type::TYPE_INTEGER;$$.calcule = true; }
                 |    TOK_REAL {$$.f = yylval.fval;$$.type = Type::TYPE_REAL;$$.calcule = true;}
                 |    TOK_BOOLEAN {$$.b = yylval.intval;$$.type = Type::TYPE_BOOL;$$.calcule = true;}
                 |    KW_NIL {/*$$.valeur = yylval.intval;*/}
                 |    TOK_STRING {$$.chet = yylval.strval;$$.type = Type::TYPE_STRING;$$.calcule = true;}
                 ;
VarExpr                :    TOK_IDENT {printf("TOK_IDENT id : %d, name : %s est ",$1.id, t.getName($1.id));
Categorie * c = $1.ts_c->getCategorie($1.id);
	if(c->TYPE == Categorie::VARIABLE){
		Variable & v = *static_cast<Variable *>(c);
		switch(v.getType()){
			case Type::TYPE_INTEGER :
				$$.type = Type::TYPE_INTEGER;
				printf("Variable entiere\n");
				break;
			case Type::TYPE_REAL :
				$$.type = Type::TYPE_REAL;
				printf("Variable reelle\n");
				break;
			case Type::TYPE_STRING :
				$$.type = Type::TYPE_STRING;
				printf("Variable String\n");
				break;
			case Type::TYPE_BOOL:
				$$.type = Type::TYPE_BOOL;
				printf("Variable Bool\n");
				break;			
		}
	}else if(c->TYPE == Categorie::PROGRAM){
		cout << "une programme\n" << endl;
	}else if(c->TYPE == Categorie::PROCEDURE){
		cout << "une procedure\n" << endl;
	}else if(c->TYPE == Categorie::FONCTION){
		cout << "une fonction\n" << endl;
	}else if(c->TYPE == Categorie::ARGUMENT){	
		cout << "un argument\n" << endl;
	}
}
                |    VarExpr SEP_CO ListIndices SEP_CF
                |    VarExpr SEP_DOT TOK_IDENT %prec OP_DOT
                |    VarExpr OP_PTR
                ;
Call                 :    TOK_IDENT Parameters
                ;
Parameters            :    SEP_PO ListParameters SEP_PF
                |    SEP_PO SEP_PF
                ;
ListIndices            :    ListIndices SEP_COMMA Expression
                |    Expression
                ;
ListParameters            :    ListParameters SEP_COMMA Expression
                |    Expression
                ;
%%
int main(int argc, char ** argv){
    if (argc > 1)
        fopen(argv[1], "r");
    yyparse();
    /*if (argc > 1)
        fclose(yyin);*/
}

